﻿using System.Linq;
using nRoute.Components;
using nRoute.Mapping;
using nRoute.Samples.Netflix.Data.Catalog;
using nRoute.Samples.Netflix.Views;
using nRoute.ViewModels;
using nRoute.Samples.Netflix.Data;
using nRoute.Components.Composition;
using System.Data.Services.Client;
using System;
using System.Diagnostics;

namespace nRoute.Samples.Netflix.ViewModels
{
    [DefineNavigationContent("Content/Netflix/Title/", typeof(TitleView))]
    [MapViewModel(typeof(TitleView))]
    public class TitleViewModel 
        : NavigationViewModelBase
    {
        private const string MOVIE_TITLE_KEY = "MovieTitle";
        private const string TITLE_FORMAT = "{0}: {1}";

        private NetflixDataContext _context;
        private ValueConverter<Genre, DataServiceQuery<Title>> _titlesInGenreConverter;
        private ValueConverter<Language, DataServiceQuery<Title>> _titlesInLanguageConverter;

        [ResolveConstructor]
        public TitleViewModel(NetflixDataContext context)
        {
            _context = context;
        }

#region Properties

        public Title MovieTitle { get; private set; }

        public ValueConverter<Genre, DataServiceQuery<Title>> GenreQueryConverter
        {
            get
            {
                if (_titlesInGenreConverter == null) _titlesInGenreConverter = CreateTitlesInGenreConverter();
                return _titlesInGenreConverter;
            }
        }

        public ValueConverter<Language, DataServiceQuery<Title>> LanguageQueryConverter
        {
            get
            {
                if (_titlesInLanguageConverter == null) _titlesInLanguageConverter = CreateTitlesInLanaguageConverter();
                return _titlesInLanguageConverter;
            }
        }

#endregion

#region Overrides

        protected override void OnInitialize(ParametersCollection state)
        {
            // if no data is passed in on initialization, it means it will be restored from state
            if (!state.ContainsKey(MOVIE_TITLE_KEY)) return;
            SetupMovieTitle((Title)state[MOVIE_TITLE_KEY]);
        }

        protected override void OnRestoreState(ParametersCollection state)
        {
            // if movie title is already loaded we don't load again
            if (MovieTitle != null) return;

            if (state.ContainsKey(MOVIE_TITLE_KEY))
            {
                SetupMovieTitle((Title)state[MOVIE_TITLE_KEY]);
            }
            else
            {
                // inform no loaded data
            }
        }

        protected override ParametersCollection OnSaveState()
        {
            if (MovieTitle != null)
            {
                return new ParametersCollection() 
                { 
                    new Parameter(MOVIE_TITLE_KEY, MovieTitle)
                };
            }
            else
            {
                return null;
            }
        }

#endregion

#region Helpers

        private void SetupMovieTitle(Title title)
        {
            base.Title = string.Format(TITLE_FORMAT, title.Type, title.Name);

            this.MovieTitle = title;
            NotifyPropertyChanged(() => MovieTitle);

            // we only load if any associated data hasn't been loaded before
            if (title.Languages.Count != 0 || title.Cast.Count != 0 || title.Directors.Count != 0
                || title.Genres.Count != 0) return;
            
            try
            {
                IAsyncResult _result1 = null;
                _result1 = _context.BeginLoadProperty(title, "Languages", (i) =>
                {
                    System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        if (_result1 != null) _context.EndLoadProperty(_result1);
                        NotifyPropertyChanged(() => Title);
                    }));
                }, null);

                IAsyncResult _result2 = null;
                _result2 = _context.BeginLoadProperty(title, "Cast", (i) =>
                {
                    System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        if (_result2 != null) _context.EndLoadProperty(_result2);
                        NotifyPropertyChanged(() => Title);
                    }));
                }, null);

                IAsyncResult _result3 = null;
                _result3 = _context.BeginLoadProperty(title, "Directors", (i) =>
                {
                    System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        if (_result3 != null) _context.EndLoadProperty(_result3);
                        NotifyPropertyChanged(() => Title);
                    }));
                }, null);

                IAsyncResult _result4 = null;
                _result4 = _context.BeginLoadProperty(title, "Genres", (i) =>
                {
                    System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        if (_result4 != null) _context.EndLoadProperty(_result4);
                        NotifyPropertyChanged(() => Title);
                    }));
                }, null);
            }
            catch
            {
                // todo: handle properly
                Debugger.Break();
            }
        }

        private ValueConverter<Genre, DataServiceQuery<Title>> CreateTitlesInGenreConverter()
        {
            return new ValueConverter<Genre, DataServiceQuery<Title>>((g) => (DataServiceQuery<Title>)(
                   from _g in _context.Genres.IncludeTotalCount()
                   from _t in _g.Titles
                   where _g.Name == g.Name
                   select _t));
        }

        private ValueConverter<Language, DataServiceQuery<Title>> CreateTitlesInLanaguageConverter()
        {
            return new ValueConverter<Language, DataServiceQuery<Title>>((l) => (DataServiceQuery<Title>)(
                   from _l in _context.Languages.IncludeTotalCount()
                   from _t in _l.Titles
                   where _l.Name == l.Name
                   select _t));
        }

#endregion

    }
}